
package com.hlkj.pay.app.common.impl;
/*
 * Hlpay-Plus aggregate payment system.
 * Copyright (c) 2024-2025 Hlpay Team Copyright has the right of final interpretation.
 */

import com.hlkj.framework.caches.service.RedisTemplateService;
import com.hlkj.framework.common.pojo.CommonResult;
import com.hlkj.framework.common.pojo.LocalContext;
import com.hlkj.framework.common.pojo.LocalRequest;
import com.hlkj.framework.common.provider.Aes256EncryptionProvider;
import com.hlkj.framework.common.util.json.JsonUtils;
import com.hlkj.framework.common.util.web.WebFrameworkUtils;
import com.hlkj.pay.app.common.IAuthCodeService;
import com.hlkj.pay.app.common.ICommonAppService;
import com.hlkj.pay.app.common.IGeetestAppService;
import com.hlkj.pay.common.AdminUserResultCode;
import com.hlkj.pay.common.CommonResultCode;
import com.hlkj.pay.common.constants.AdminConstant;
import com.hlkj.pay.config.SmsTemplateProperties;
import com.hlkj.pay.dto.admin.AdminUserDto;
import com.hlkj.pay.enums.AdminUserEnum;
import com.hlkj.pay.infrastructure.model.admin.AdminUserDO;
import com.hlkj.pay.service.admin.IAdminUserService;
import com.hlkj.pay.util.TokenUtils;
import com.hlkj.pay.vo.admin.req.user.AdminAuthCodeReq;
import com.hlkj.pay.vo.common.req.SmsSendReq;
import com.hlkj.pay.vo.common.resp.token.TokenInfoResp;
import jodd.util.StringUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import javax.servlet.http.HttpServletRequest;
import java.security.SecureRandom;

/**
 * @author HlpayTeam
 * @date 2024/09/14 15:38
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class AuthCodeServiceImpl implements IAuthCodeService {

    private final RedisTemplateService redisTemplateService;

    private final ICommonAppService commonAppService;

    @Value("${auth.authCode.expireTime}")
    private Integer authCodeExpireTime;

    private final SmsTemplateProperties smsTemplateProperties;

    private final IGeetestAppService geetestAppService;

    private final IAdminUserService adminUserService;

    private final Aes256EncryptionProvider aes256EncryptionProvider;

    @Value("${auth.signingKey}")
    private String signingKey;

    @Override
    public CommonResult<Void> sendAuthCode(AdminAuthCodeReq adminAuthCodeReq) {
        if (adminAuthCodeReq.getVerification() != null) {
            CommonResult<Boolean> validate = geetestAppService.validate(adminAuthCodeReq.getVerification());
            if (!validate.isSuccess()) {
                // return CommonResult.error(validate.getCode(),validate.getMsg());
            }
        }
        AdminUserEnum.AdminAuthCodeEnum authCodeEnum = AdminUserEnum.AdminAuthCodeEnum.from(adminAuthCodeReq.getBusinessScenario());
        String phone = adminAuthCodeReq.getPhone();
        switch (authCodeEnum) {
            case ADMIN_AUTH_CODE_LOGIN:
            case ADMIN_AUTH_CODE_FORGET_PWD:
                try {
                    AdminUserDto adminUserDto = adminUserService.queryUserByPhone(adminAuthCodeReq.getPhone(), null);
                    if (adminUserDto == null) {
                        return CommonResult.error(AdminUserResultCode.USER_PHONE_DOES_NOT_EXIST);
                    }
                }
                catch (Exception e) {
                    return CommonResult.error(CommonResultCode.DATA_ENCRYPT_ERROR);
                }
                break;
            // 给商户管理员发验证码
            case MERCHANT_PAYMENT:
                phone = processMerchantRootUserPhone(adminAuthCodeReq.getBusinessParams());
                break;
            default:
                if (StringUtil.isEmpty(adminAuthCodeReq.getPhone())) {
                    phone = processLoginUserPhone();
                    break;
                }
        }
        if (!StringUtils.hasText(phone)) {
            return CommonResult.error(AdminUserResultCode.USER_PHONE_DOES_NOT_EXIST);
        }
        String userAuthCodeKey = authCodeKey(phone, authCodeEnum);
        String code = getRandomCode(userAuthCodeKey);
        Object object = redisTemplateService.get(userAuthCodeKey);
        if (object != null) {
            log.info("authCode 未过期 从新发送 phone:{} code:{}", phone, code);
            code = object.toString();
        }
        SmsSendReq smsSendReq = new SmsSendReq();
        smsSendReq.setCode(code);
        log.info("authCode:{}", code);
        smsSendReq.setTemplateCode(templateCode(authCodeEnum));
        switch (authCodeEnum) {
            // case AUTH_CODE_FORGET_PWD:
            // smsSendReq.setSn(adminAuthCodeReq.getSn());
            // smsSendReq.setCode(adminAuthCodeReq.getPwd());
            // smsSendReq.setAccount(adminAuthCodeReq.getAccount());
            // break;
            case ADMIN_AUTH_CODE_UPDATE_PWD:
                LocalRequest localUserRequest = LocalContext.get();
                // smsSendReq = new SmsSendReq();
                smsSendReq.setUserId(localUserRequest.getUserId());
                smsSendReq.setPhone(phone);
                break;
            case ADMIN_AUTH_CODE_REGISTER:
                smsSendReq.setPhone(phone);
                break;
            default:
                smsSendReq.setPhone(phone);
                break;
        }
        CommonResult<String> commonResult = commonAppService.sendSms(smsSendReq);
        if(!commonResult.isSuccess()){
            return CommonResult.error(commonResult.getCode(),commonResult.getMsg());
        }
        redisTemplateService.set(userAuthCodeKey, code, expireTime(authCodeEnum));
        return CommonResult.success();
    }

    private String getRandom() {
        long timestampPart = System.currentTimeMillis() % 10000;
        int randomPart = new SecureRandom().nextInt(100);
        return String.format("%04d%02d", timestampPart, randomPart);
    }

    //获取验证码，1小时内不重复
    private String getRandomCode(String userAuthCodeKey) {
        String redisKey = userAuthCodeKey + ".getRandomCode";
        String randomNum = getRandom();

        for (int i = 0; i <10; i++) {
            Object o = redisTemplateService.get(redisKey);
            if (o != null && randomNum.equals(o.toString())) {
                randomNum = getRandom();
            } else {
                break;
            }
        }
        redisTemplateService.set(redisKey, randomNum, 1800);
        return randomNum;
    }

    @Override
    public CommonResult<Void> checkAuthCode(String phone, AdminUserEnum.AdminAuthCodeEnum authCodeEnum, String code) {
        String userAuthCodeKey = authCodeKey(phone, authCodeEnum);
        Object object = redisTemplateService.get(userAuthCodeKey);
        if (object == null) {
            return CommonResult.error(CommonResultCode.AUTH_CODE_ERROR);
        }
        if (!object.toString().equals(code)) {
            return CommonResult.error(CommonResultCode.AUTH_CODE_ERROR);
        }
        // redisTemplateService.del(userAuthCodeKey);
        return CommonResult.success();
    }

    String authCodeKey(String account, AdminUserEnum.AdminAuthCodeEnum authCodeEnum) {
        StringBuilder sb = new StringBuilder(authCodeEnum.getRedisCachePrefix());
        sb.append(account);
        return sb.toString();
    }

    Integer expireTime(AdminUserEnum.AdminAuthCodeEnum authCodeEnum) {
        return authCodeExpireTime;
    }

    String templateCode(AdminUserEnum.AdminAuthCodeEnum authCodeEnum) {
        switch (authCodeEnum) {
            case ADMIN_AUTH_CODE_FORGET_PWD:
                return smsTemplateProperties.getForgetPwd();
            case ADMIN_AUTH_CODE_UPDATE_PWD:
                return smsTemplateProperties.getUpdatePwd();
            case ADMIN_AUTH_CODE_REGISTER:
                return smsTemplateProperties.getRegister();
            case ADMIN_AUTH_CODE_UPDATE_PHONE:
                return smsTemplateProperties.getUpdatePhone();
            default:
                return smsTemplateProperties.getLogin();
        }
    }

    String processLoginUserPhone() {
        Long userId = userId();
        if (userId == null) {
            return null;
        }
        AdminUserDO adminUserDO = adminUserService.queryUser(userId);
        if (adminUserDO == null) {
            return null;
        }
        try {
            return aes256EncryptionProvider.decryptString(adminUserDO.getPhoneEncrypt());
        }
        catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    String processMerchantRootUserPhone(String sn) {
        if (!StringUtils.hasText(sn)) {
            return null;
        }
        AdminUserDO adminUserDO = adminUserService.queryUserBySn(sn);
        if (adminUserDO == null) {
            return null;
        }
        try {
            return aes256EncryptionProvider.decryptString(adminUserDO.getPhoneEncrypt());
        }
        catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    private Long userId() {
        HttpServletRequest request = WebFrameworkUtils.getRequest();
        // token 获取
        String header = request.getHeader(AdminConstant.AUTHORIZATION);
        if (StringUtil.isEmpty(header)) {
            return null;
        }
        String token = header.replace(AdminConstant.BEARER_BANK, "");
        token = token.replace(AdminConstant.BEARER, "");
        if (StringUtil.isEmpty(token)) {
            return null;
        }
        boolean checkToken = TokenUtils.checkToken(token, signingKey);
        if (!checkToken) {
            return null;
        }
        TokenInfoResp accessTokenInfo = TokenUtils.parseToken(token);
        if (log.isDebugEnabled()) {
            log.debug("TokenInfo:{}", JsonUtils.toJsonString(accessTokenInfo));
        }
        return accessTokenInfo.getUserId();
    }
}
